#include "client_sock.h"
#include <stdio.h>
#ifndef _WIN32
#include <stdlib.h>

#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#define SOCKET_ERROR            (-1)
#endif // _WIN32
#define  SIZE_KB(x)  x<<10
#ifdef _WIN32
#include <windows.h>
#include<time.h>
typedef int socklen_t;
//#include "cross_com.h"
#else
int GetLastError()
{
    return errno;
}
#endif


#define ERR_PARAM       -101,	//´íÎóµÄ²ÎÊý
#define SENDBUF_FULL	-300
#define SENDBUF_ERROR	-301
#define SENDBUF_OK		-302
#define SENDBUF_TIMEOUT	-303
#define SENDBUF_INTERRUPT	-304
#define ERR_SOCK_TIME_OUT -400//³¬Ê±
#define ERR_SOCK_FD       -401

CClientSock::CClientSock()
{
    #ifdef _WIN32
	static bool bInit = false;
	if( ! bInit)
	{
		WORD sockVersion = MAKEWORD(2,2);
		WSADATA data;
		if(WSAStartup(sockVersion, &data) != 0)
		{
			return;
		}
		bInit = true;
	}
    #endif // _WIN32
	m_rcv_buf = NULL;
	m_sbuf = NULL;
	m_sbuf_sz = SIZE_KB(32);
	m_sbuf = new char[m_sbuf_sz];
	memset(m_url,0,256);
}

CClientSock::~CClientSock()
{
	if(NULL != m_rcv_buf )
	{
		delete [] m_rcv_buf;
	}

	if( NULL != m_sbuf)
	{
		delete [] m_sbuf;
	}
}

int CClientSock::Connect(const  char * IP, int portNum,int is_tcp)
{
	int ret = 0;

	if (is_tcp == 0)
	{
		m_sclient = socket(AF_INET, SOCK_DGRAM, 0);
		if (m_sclient < 0)
		{
			printf("create socket fail!\n");
			return -1;
		}

		memset(&m_serAddr, 0, sizeof(m_serAddr));
		m_serAddr.sin_family = AF_INET;
		m_serAddr.sin_port = htons(portNum);
#ifdef _WIN32
		m_serAddr.sin_addr.S_un.S_addr = inet_addr(IP);
		unsigned long on = 1;  //此值为0，会将套接字设置为阻塞(默认的)；为1设置为非阻塞
		if (0 != ioctlsocket(m_sclient, FIONBIO, &on))
		{
			/* Handle failure. */
		}

#else
		m_serAddr.sin_addr.s_addr = inet_addr(IP);
		int flag = fcntl(m_sclient, F_GETFL, 0);  //取标志
		if (flag < 0) {
				
			perror("fcntl F_GETFL fail");
			//return -1;
		}
		if (fcntl(m_sclient, F_SETFL, flag | O_NONBLOCK) < 0) {  //设置标志
			perror("fcntl F_SETFL fail");
		}

#endif // _WIN32
		//本地信息绑定到套接字中，相当于告诉套接字本机要接受数据

		m_protoType = IPPROTO_UDP;
		m_bUDP = true;
		return 0;
	}

	m_sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(m_sclient == -1)
	{
		printf("invalid socket !");
		return -1;
	}
	printf("Connect IP %s port %d .\n",IP,portNum);
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(portNum);

	#ifdef _WIN32
	serAddr.sin_addr.S_un.S_addr = inet_addr(IP);
	#else
    serAddr.sin_addr.s_addr = inet_addr(IP);
	#endif

	strcpy(m_ip,IP);
	m_port = portNum;

	if (connect(m_sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == -1)
	{
		int err = errno;
		printf("connect error err %d !",err);
		Close();
		return -1;
	}
	int nZero=0;
	setsockopt(m_sclient,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));

	m_protoType = IPPROTO_TCP;
	m_connect_status = true;

	return ret;
}


 int CClientSock::ConnectUrl(char * url ,int port)
 {
	if(url == NULL)
		return -EINVAL;

	printf("just_sock_connect_url(%s:%d)", url, port);
	strcpy(m_url,url);
	m_port = port;
	struct hostent *hptr = gethostbyname(url);
	if(hptr == NULL)
        return -EFAULT;

	printf("Address type: %s\n", (hptr->h_addrtype==AF_INET) ? "AF_INET": "AF_INET6");
    //IP地址
    for(int i=0; hptr->h_addr_list[i]; i++){
		printf("IP addr %d: %s\n", i+1, inet_ntoa( *(struct in_addr*)hptr->h_addr_list[i] ) );
    }

	int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(sockfd <= 0){
		printf("socket error");
		return -EFAULT;
	}

	struct sockaddr_in servaddr;
	memset((void*)&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(port);
	//inet_pton(AF_INET, ipBuf, &servaddr.sin_addr.s_addr);
	servaddr.sin_addr.s_addr = *((unsigned long*)hptr->h_addr_list[0]);

	int reval = connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
	if (reval == 0)
	{
	    m_sclient = sockfd;
		m_connect_status = true;
		int nZero=0;
		setsockopt(m_sclient,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));
		printf("m_sclient  %d connect [%s:%d] Success!",m_sclient, url, port);
	    return sockfd;
	}
    m_connect_status = false;
	switch(errno)
	{
	case EISCONN:
		reval = 0;
		break;
	case EINPROGRESS:
	case EALREADY:
		reval = socket_wait_writable(sockfd, 30000);
		break;
	default:
		reval = -EFAULT;
		break;
	}
	if (reval < 0)
	{
		printf("connect [%s:%d] failed, reval=%d", url, port, reval);
#ifdef _WIN32
		Close(sockfd);
#else
		close(sockfd);
#endif
		return reval;
	}
	printf("connect [%s:%d] Success!", url, port);

	return sockfd;
 }

 int CClientSock::socket_wait_writable(int sockfd, int timeout_ms)
 {
	 struct timeval tpstart,tpend,timeout_v;
	 fd_set wset, eset;
	 long time_used = 0;
	 int reval = 0;
#ifndef _WIN32
	 printf("socket_wait(sockfd=%d,timeout_ms=%d)", sockfd, timeout_ms);

	 gettimeofday(&tpstart,NULL);

	 while(1)
	 {
		 gettimeofday(&tpend,NULL);
		 time_used=1000*(tpend.tv_sec-tpstart.tv_sec);
		 time_used += (tpend.tv_usec-tpstart.tv_usec)/1000;
		 if (time_used > timeout_ms)
		 {
			 printf("write timeout1 [%d ms]", timeout_ms);
			 return -ETIME;
		 }
		 long time_seft = timeout_ms - time_used;
		 timeout_v.tv_sec = time_seft/1000;
		 timeout_v.tv_usec = time_seft%1000 * 1000;

		 FD_ZERO(&wset);
		 FD_ZERO(&eset);
		 FD_SET(sockfd, &wset);
		 FD_SET(sockfd, &eset);
		 reval = select(sockfd + 1, NULL, &wset, &eset , &timeout_v);
		 if(reval == -1 && errno != EINTR)
		 {
			 printf("error in socket_wait 1:%s", strerror(errno));
			 return -EFAULT;
		 }
		 else if( reval == 0)
		 {
			 printf("write timeout2 ");//[%d ms]", JUST_SOCK_DEFAULT_TIMEOUT);
			 return -ETIME;
		 }
		 else if(reval > 0 && FD_ISSET(sockfd, &eset))
		 {
			 printf("error in socket_wait 2:%s", strerror(errno));
			 return -EFAULT;
		 }
		 else if ( reval > 0 && FD_ISSET(sockfd, &wset))
		 {
			 //write event:
			 printf("socket[%d] writable ...", sockfd);
			 break;
		 }
		 else
		 {
			 printf("unexpected err in socket_wait");
		 }
	 }
#endif
	 return 0;
 }


 int CClientSock::Reconnect()
 {

	 if(strlen(m_url) != 0)
	 {
		return	ConnectUrl(m_url,m_port);
	 }

	 int ret = 0;
	 m_sclient = socket(AF_INET, SOCK_STREAM, m_protoType);
	 if(m_sclient == -1)
	 {
		 printf("invalid socket !");
		 return -1;
	 }

	 sockaddr_in serAddr;
	 serAddr.sin_family = AF_INET;
	 serAddr.sin_port = htons(m_port);
    #ifdef _WIN32
	serAddr.sin_addr.S_un.S_addr = inet_addr(m_ip);
	#else
    serAddr.sin_addr.s_addr = inet_addr(m_ip);
	#endif
	printf("Reconnect connect ip %s port %d ",m_ip,m_port );
	 if (connect(m_sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == -1)
	 {
		 int err = errno;
		 printf("connect ip %s port %d error err %d !",m_ip,m_port,err);
		 Close();
		 return -1;
	 }

	 return ret;
 }

 int CClientSock::Close(int sock)
 {
	 if(sock != -1 )
	 {
#ifdef _WIN32
		 closesocket(sock);

#else
		 close(sock);

#endif
		 return 0;
	 }
#ifdef _WIN32
	 closesocket(m_sclient);
	 m_sclient = INVALID_SOCKET;
#else
	 close(m_sclient);
	  m_sclient = -1;
#endif
	 return 0;
 }

int CClientSock::DisConnect()
{
	int ret = 0;

	Close();

	return ret;
}

int CClientSock::SendTimeOut(char* buf, uint32_t len, int time)
{
	int offset = 0;
	while(time--){
		int ret = onSend(buf+offset, len-offset, offset);
		if (SENDBUF_OK == ret && offset == 0){
			return 0;
		}
		else if(SENDBUF_ERROR == ret) return SENDBUF_ERROR;
		else if(SENDBUF_INTERRUPT == ret) continue;
	}
	return SENDBUF_TIMEOUT;
}

int CClientSock::onSend(char* buf, uint32_t len, int& offset)
{

	FD_ZERO( &mFdw );
	FD_SET( m_sclient, &mFdw );
	timeval     mTimeOut;
	mTimeOut.tv_sec = 0;
	mTimeOut.tv_usec = 100000;
	int flags = select( m_sclient + 1, NULL, &mFdw, NULL, &mTimeOut );
	if (flags == 0)
	{
		return SENDBUF_FULL;
	}
	else if (flags < 0)
	{
		return SENDBUF_ERROR;
	}
	else
	{
		int sendLen = send(m_sclient, buf, len, 0);
		if(sendLen == len)	offset = 0;
		else if (sendLen > 0)
			offset += sendLen;
		else{
			if(errno == EINTR || errno == EAGAIN)
				return SENDBUF_INTERRUPT;
			else
				return SENDBUF_ERROR;
		}
	}
	return SENDBUF_OK;
}

int CClientSock::SendRawData(char * buf, int buf_sz)
{
	int ret = 0;
	if (m_bUDP)
	{
		socklen_t len;
		len = sizeof(m_serAddr);
		ret = sendto(m_sclient, buf, buf_sz, 0, (struct sockaddr*)&m_serAddr, len);
		if (ret < 0)
		{
			int err = GetLastError();
			printf("Send data error  %d .\n ", err);
		}
	}
	else
	{
		ret = send(m_sclient, buf, buf_sz, 0);
		if (ret < 0)
		{
			int err = GetLastError();
			printf("Send data error  %d .\n ", err);
		}
		else if (ret != buf_sz)
		{
			printf("Send data size  %d  errno %d .\n ", ret, errno);
			ret = ERR_SOCK_FD;
		}
	}

	return ret;
}

 int CClientSock::RcvData(char * buf ,int buf_sz)
 {
	//char recData[8] = {0};
	 int ret = 0;
	 if (m_bUDP)
	 {
		 socklen_t len;
		 len = sizeof(m_serAddr);
		 ret = recvfrom(m_sclient, buf, buf_sz, 0, (struct sockaddr*)&m_serAddr, &len);
		 if (ret < 0)
		 {
			 int err = GetLastError();
			// printf("get data error  %d .\n ", err);
		 }
		 return ret;
	 }

	 fd_set      mFdr;
	 FD_ZERO( &mFdr );
	 FD_SET( m_sclient, &mFdr );
	timeval  mTimeOut;
	mTimeOut.tv_sec = 1;
	mTimeOut.tv_usec = 10000;
	int flags = select( m_sclient + 1, &mFdr, NULL, NULL, &mTimeOut );
	if( flags > 0 ) {
	{
		buf_sz = 32 << 10;
		ret = recv(m_sclient, buf, buf_sz, 0);
		if (ret < 0)
		{
			int err = GetLastError();
			printf("Send data error  %d .\n ", err);
			return -1;
		}

	}
	}else if ( flags < 0 )
	{
		return ERR_SOCK_FD;
	}
	else
	{
		return ERR_SOCK_TIME_OUT;
	}
	return ret;
 }

 void CClientSock::SetTimeOut(int tmMs)
 {
	 struct timeval timeout = {3,0};

	timeout.tv_sec  = tmMs / 1000;
	timeout.tv_usec = (tmMs % 1000) *1000;

	setsockopt(m_sclient,SOL_SOCKET,SO_RCVTIMEO,(char *)&timeout,sizeof(struct timeval));

 }



